//
//  FYDaoBanCheck.m
//  JailBreakCheck
//
//  Created by Sean on 15/8/18.
//  Copyright (c) 2015年 FeiYu. All rights reserved.
//

#import "FYDBCheck.h"
#import <UIKit/UIKit.h>
#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <TargetConditionals.h>

#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0])
#define USER_APP_PATH @"/User/Applications/"

/* The encryption info struct and constants are missing from the iPhoneSimulator SDK, but not from the iPhoneOS or
 * Mac OS X SDKs. Since one doesn't ever ship a Simulator binary, we'll just provide the definitions here. */
#if TARGET_IPHONE_SIMULATOR && !defined(LC_ENCRYPTION_INFO)
#define LC_ENCRYPTION_INFO 0x21
struct encryption_info_command {
    uint32_t cmd;
    uint32_t cmdsize;
    uint32_t cryptoff;
    uint32_t cryptsize;
    uint32_t cryptid;
};
#endif

int main (int argc, char *argv[]);

@implementation FYDBCheck

+ (BOOL)isYY {
    if ([FYDBCheck isYY_1]) {
        return YES;
    }
    if ([FYDBCheck isYY_2]) {
        return YES;
    }
    if ([FYDBCheck isYY_3]) {
        return YES;
    }
    if ([FYDBCheck isYY_4]) {
        return YES;
    }
    return NO;
}

+ (BOOL)isPJ {
    @try {
        if ([[[NSBundle mainBundle] infoDictionary] objectForKey:@"SignerIdentity"]) {
            
            return YES;
        }
        
        if (![FYDBCheck _fileExistMainBundle:@"_CodeSignature"]) {
            
            return YES;
        }
        
        if (![FYDBCheck _fileExistMainBundle:@"CodeResources"]) {
            
            return YES;
        }
        
        if (![FYDBCheck _fileExistMainBundle:@"ResourceRules.plist"]) {
            
            return YES;
        }
        
        if ([FYDBCheck isEncryptionCracked]) {
            
            return YES;
        }
    }
    @catch (NSException *exception) {
        
    }
    @finally {
        
    }
    //you may test binary's modify time ...but,
    //if someone want to crack your app, this method is useless..
    //you need to change this method's name and do more check..
    return NO;
}

const char* jailbreak_tool_pathes[] = {
    "/Applications/Cydia.app",
    "/Library/MobileSubstrate/MobileSubstrate.dylib",
    "/bin/bash",
    "/usr/sbin/sshd",
    "/etc/apt"
};

//判定常见的越狱文件
+ (BOOL)isYY_1 {
    for (int i=0; i<ARRAY_SIZE(jailbreak_tool_pathes); i++) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithUTF8String:jailbreak_tool_pathes[i]]]) {
            
            return YES;
        }
    }
    
    return NO;
}

//判断cydia的URL scheme
+ (BOOL)isYY_2 {
    
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://"]]) {
        
        return YES;
    }
    
    return NO;
}

//读取系统所有应用的名称
+ (BOOL)isYY_3 {
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:USER_APP_PATH]) {
        
//        NSArray *applist = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:USER_APP_PATH error:nil];
        
        return YES;
    }
    
    return NO;
}

//读取环境变量
char* printEnv(void) {
    char *env = getenv("DYLD_INSERT_LIBRARIES");

    return env;
}

+ (BOOL)isYY_4 {
    if (printEnv()) {
        
        return YES;
    }
    
    return NO;
}

+ (BOOL)_fileExistMainBundle:(NSString *)name {
    NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
    NSString *path = [NSString stringWithFormat:@"%@/%@", bundlePath, name];
    return [[NSFileManager defaultManager] fileExistsAtPath:path];
}

+ (BOOL)isEncryptionCracked {
    
    //http://stackoverflow.com/questions/4123162/how-to-programmatically-determine-if-the-app-has-been-cracked-or-pirated
    //http://landonf.bikemonkey.org/code/iphone/iPhone_Preventing_Piracy.20090213.html
    const struct mach_header *header;
    Dl_info dlinfo;
    
    /* Fetch the dlinfo for main() */
    if (dladdr(main, &dlinfo) == 0 || dlinfo.dli_fbase == NULL) {
        NSLog(@"Could not find main() symbol (very odd)");
        return NO;
    }
    header = dlinfo.dli_fbase;
    
    /* Compute the image size and search for a UUID */
    struct load_command *cmd = (struct load_command *) (header+1);
    
    for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) {
        /* Encryption info segment */
        if (cmd->cmd == LC_ENCRYPTION_INFO) {
            struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) cmd;
            /* Check if binary encryption is enabled */
            if (crypt_cmd->cryptid < 1) {
                /* Disabled, probably pirated */
                return YES;
            }
            
            /* Probably not pirated? */
            return NO;
        }
        
        cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);
    }
    
    /* Encryption info not found */
    return YES;
}

@end
